package cn.echo.consul.manager.service.impl;

import cn.echo.consul.manager.entity.Claim;
import cn.echo.consul.manager.entity.Token;
import cn.echo.consul.manager.service.AccessTokenService;
import cn.echo.consul.manager.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.jwt.crypto.sign.SignerVerifier;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.Map;

/**
 * 访问token实现类
 *
 * @author lonyee
 */
@Slf4j
@Service
public class AccessTokenServiceImpl implements AccessTokenService {

    @Value("${manager.jwt.secret}")
    private String secret;

    final long defaultExpire = 2 * 60 * 60L; //默认过期时间为2小时


    /**
     * 加密生成token
     *
     * @param payload 加密传输内容
     * @return token
     */
    public <T> Token genToken(T payload) {
        return this.genToken(payload, 0L);
    }

    /**
     * 加密生成token
     *
     * @param payload 加密传输内容
     * @param expire  过期时间 秒
     * @return token
     */
    public <T> Token genToken(T payload, Long expire) {

        long expiration = expire == null || expire <= 0L ? this.defaultExpire : expire;

        //get crypto
        long timestamp = Instant.now().getEpochSecond();
        //装载数据
        Claim<T> claim = new Claim<>();
        claim.setIssuedAt(timestamp);
        claim.setRefreshAt(timestamp);
        claim.setExpiration(expiration);
        claim.setPayload(payload);

        //MacSigner/RsaSigner
        SignerVerifier signerVerifier = new MacSigner(secret);
        String jsonClaimVo = JsonUtil.toJson(claim);
        Jwt jwt = JwtHelper.encode(jsonClaimVo, signerVerifier);
        return new Token(expiration, jwt.getEncoded());
    }

    /**
     * 刷新获取新token
     *
     * @param token 加密串token
     * @return token
     */
    public Token refreshToken(String token) {
        if (token == null) {
            return null;
        }
        //MacSigner/RsaSigner
        SignerVerifier signerVerifier = new MacSigner(secret);
        Jwt jwt = JwtHelper.decodeAndVerify(token, signerVerifier);
        Claim claim = JsonUtil.fromJson(jwt.getClaims(), Claim.class);

        long nowTimestamp = Instant.now().getEpochSecond();
        //判断是否过期，过期返回空
        if (claim == null || nowTimestamp >= (claim.getRefreshAt() + claim.getExpiration())) {
            return null;
        }

        //get crypto
        claim.setRefreshAt(nowTimestamp);
        claim.setPayload(claim.getPayload());
        Jwt jws = JwtHelper.encode(JsonUtil.toJson(claim), signerVerifier);
        return new Token(claim.getExpiration(), jws.getEncoded());
    }

    /**
     * 验证token
     *
     * @param token 加密串token
     * @return boolean
     */
    public boolean verifyToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return false;
        }
        //MacSigner/RsaSigner
        SignerVerifier signerVerifier = new MacSigner(secret);
        Jwt jwt = JwtHelper.decodeAndVerify(token, signerVerifier);
        Claim claim = JsonUtil.fromJson(jwt.getClaims(), Claim.class);
        if (claim == null) {
            return false;
        }

        long nowTimestamp = Instant.now().getEpochSecond();
        //判断是否通过
        return nowTimestamp < (claim.getRefreshAt() + claim.getExpiration());
    }

    /**
     * 解密token获取携带数据
     *
     * @param token 加密串token
     * @return payload
     */
    public <T> T getPayload(String token, Class<T> clazz) {
        Claim<T> claim = this.getClaim(token);
        return claim == null ? null : JsonUtil.mapToBean((Map)claim.getPayload(), clazz);
    }

    /**
     * 解密token获取Claim
     *
     * @param token 加密串token
     * @return payload
     */
    public Claim getClaim(String token) {

        if (Strings.isEmpty(token)) {
            return null;
        }
        Jwt jwt = JwtHelper.decode(token);
        Claim claim = JsonUtil.fromJson(jwt.getClaims(), Claim.class);

        long nowTimestamp = Instant.now().getEpochSecond();
        //判断是否过期，过期返回空
        if (claim == null || nowTimestamp >= (claim.getRefreshAt() + claim.getExpiration())) {
            return null;
        }
        return claim;
    }

    /**
     * 解密token获取过期时间（不校验） expire
     *
     * @param token 加密串token
     * @return expire
     */
    public long getExpire(String token) {

        if (Strings.isEmpty(token)) {
            return 0L;
        }
        //不进行校验直接获取
        Jwt jwt = JwtHelper.decode(token);
        Claim claim = JsonUtil.fromJson(jwt.getClaims(), Claim.class);

        long nowTimestamp = Instant.now().getEpochSecond();
        //判断是否过期，过期返回空
        if (claim == null || nowTimestamp >= (claim.getRefreshAt() + claim.getExpiration())) {
            return 0L;
        }
        return claim.getExpiration();
    }


    /**
     * 获取短token，降低存储空间
     */
    public String shortToken(String token) {
        return token.substring(token.lastIndexOf(".") + 1);
    }
}
